package chordrouting;

import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Endstart
 * Date: 2020-09-07
 * Time: 22:24
 */
public class ChordRouting3 {

    private static final int SPACE_SIZE = 9;
    private static   Scanner scanner = new Scanner(System.in);

    private int spaceSize;
    private List<Node> nodeList;

    public static void main(String[] args) {
        System.out.println("==========================================================");
        System.out.println("              Distributed Hash Table");
        System.out.println("==========================================================");
        System.out.print("Input node numbers first, separated by comma: ");
        String line = scanner.nextLine();
        ChordRouting3 chordRouting = new ChordRouting3();
        chordRouting.init(SPACE_SIZE, line);

        while (true) {
            System.out.println("select choices: ");
            System.out.println("A. add node");
            System.out.println("B. get node");
            System.out.println("C. show all nodes");
            System.out.println("D. get key value");
            System.out.println("E. add key value");
            System.out.println("F. remove key value");
            System.out.println("Q. quit");
            System.out.print("select: ");


            String input = scanner.nextLine();
            if (input.equalsIgnoreCase("A")) {
                chordRouting.addNode();
            } else if (input.equalsIgnoreCase("B")) {
                chordRouting.getNode();
            } else if (input.equalsIgnoreCase("C")) {
                for (Node node : chordRouting.nodeList) {
                    System.out.println(node);
                }
            } else if (input.equalsIgnoreCase("D")) {
                chordRouting.getKeyValue();
            } else if (input.equalsIgnoreCase("E")) {
                chordRouting.addKeyValue();
            } else if (input.equalsIgnoreCase("F")) {
                chordRouting.removeKeyValue();
            } else if (input.equalsIgnoreCase("Q")) {
                System.out.println("See you!");
                return;
            }
            System.out.println();

        }
    }

    public Node searchKey(int hashKey) {
        Node startNode = nodeList.get(0);

//        System.out.println("Start search: " + startNode.getNodeName());
        if (hashKey < startNode.getValue()) {
            // key小于开始节点值时，不能判定当前节点是否key的后继节点，需要从第一个节点开始
//            System.out.println("Key value is lower than start node, than search from origin node: " + nodeList.get(0).getNodeName());
            return search(hashKey, nodeList.get(0));
        } else {
            return search(hashKey, startNode);
        }
    }

    public boolean addKeyValue() {
        System.out.print("Input key: ");
        String key = scanner.nextLine();
        System.out.print("Input value: ");
        String value = scanner.nextLine();
        int hashKey = hash(key);
        Node node = searchKey(hashKey);
        node.putKeyValue(key, value);
        System.out.println("add to " + node.getNodeName());
        return true;
    }

    public void getKeyValue() {
        System.out.print("Input node key: ");
        String key = scanner.nextLine();
        int hashKey = hash(key);
        Node node = searchKey(hashKey);
        if (!node.getKeyValueMap().containsKey(key)) {
            System.out.println("No value for key " + key);
        } else {
            System.out.println("Value: " + node.getKeyValue(key));
        }
    }

    public void removeKeyValue() {
        System.out.print("Input node key: ");
        String key = scanner.nextLine();
        int hashKey = hash(key);
        Node node = searchKey(hashKey);
        if (!node.getKeyValueMap().containsKey(key)) {
            System.out.println("No value for key " + key);
        } else {
            node.removeKeyValue(key);
            System.out.println("Remove key value from " + node.getNodeName());
        }
    }

    public void addNode() {
        System.out.print("Input node number: ");
        int value = scanner.nextInt();
        scanner.nextLine();
        int max = (int) Math.pow(2, spaceSize)-1;
        if (value > max || value < 0) {
            System.out.println("Node number should be 0~" + max);
            return;
        }
        if (getNodeByValue(value)!=null) {
            System.out.println("Node " + value + "has exists");
            return;
        }

        int index = -1;
        for (index = 0; index< nodeList.size();index++) {
            if (nodeList.get(index).getValue() > value) {
                break;
            }
        }
        Node newNode = new Node(value);
        genFingerTable(newNode);
        nodeList.add(index, newNode);

        // 更新其他节点的fingerTable
        Node successorNode = nodeList.get((index + 1) % nodeList.size());
        for (Node node : nodeList) {
            if (node.getFingerTable().containsValue(successorNode)) {
                genFingerTable(node);
            }
        }

        // 后继节点successorNode时的ke-value要分割
        for (Map.Entry<Object, Object> entry : successorNode.getKeyValueMap().entrySet()) {
            if (hash(entry.getKey()) <= newNode.getValue()) {
                newNode.putKeyValue(entry.getKey(), entry.getValue());
            }
        }
        for (Map.Entry<Object, Object> entry : newNode.getKeyValueMap().entrySet()) {
            successorNode.removeKeyValue(entry.getKey());
        }

        System.out.println(newNode);
    }

    /**
     * get node
     */
    private void getNode() {
        System.out.print("Input node number: ");
        int value = scanner.nextInt();
        scanner.nextLine();
        Node node = getNodeByValue(value);
        if (node == null) {
            System.out.println("No node for value " + value);
        } else {
            System.out.println(node.toString());
        }
    }

    public int hash(Object key) {
        int hash = key.hashCode();
        return Math.abs(hash) % (int) Math.pow(2, spaceSize);
    }

    private Node search(int key, Node node) {
//        System.out.println("---- search node: " + node.getNodeName());

        if (key <= node.getValue()) {
//            System.out.println("---- search success: " + node.getNodeName());
            return node;
        }

        Node successorNode = successor(node);
        if (key<=successorNode.getValue()) {
//            System.out.println("---- search success: " + successorNode.getNodeName());
            return successorNode;
        }

        LinkedHashMap<Integer, Node> fingerTable = node.getFingerTable();
        List<Integer> indexFingerList = new ArrayList<>(fingerTable.keySet());
        // 从最远的后继节点开始比较
        for (int i = indexFingerList.size() - 1; i >= 0; i--) {
            int finger = indexFingerList.get(i);
            Node nodeTmp = fingerTable.get(finger);
            if (nodeTmp.getValue() > node.getValue() && nodeTmp.getValue() <= key) {
                return search(key, nodeTmp);
            }
        }

        return nodeList.get(0);
    }

    /**
     * 获取某一节点的后继节点
     * @param node
     * @return
     */
    private Node successor(Node node) {
        List<Integer> indexFingerList = new ArrayList<>(node.getFingerTable().keySet());
        return node.getFingerTable().get(indexFingerList.get(0));
    }

    public void init(int spaceSize, String numberLine) {
        this.spaceSize = spaceSize;
        String[] strings = numberLine.split(",");
        List<Integer> nodeValueList = new ArrayList<>();
        try {
            for (String string : strings) {
                nodeValueList.add(Integer.parseInt(string));
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("Input number error!");
        }
        if (nodeValueList.size() == 0) {
            throw new IllegalArgumentException("No number input");
        }
        Collections.sort(nodeValueList);

        this.nodeList = new ArrayList<>();
        int maxValue = (int) Math.pow(2, spaceSize) - 1;
        for (Integer integer : nodeValueList) {
            if (integer > maxValue) {
                throw new IllegalArgumentException("space is not satisfied, all number should be 0~" + maxValue);
            }
            nodeList.add(new Node(integer));
        }

        for (Node node : nodeList) {
            genFingerTable(node);
        }

    }

    /**
     * 构建节点的FingerTable
     * @param node
     */
    private void genFingerTable(Node node) {
        int maxValue = (int) Math.pow(2, spaceSize) - 1;
        int value = node.getValue();
        LinkedHashMap<Integer, Node> table = new LinkedHashMap<>();
        for (int i = 1; i <= spaceSize; i++) {
            int indexAdd = (int) Math.pow(2, i - 1);
            int valueTmp = (value + indexAdd) % maxValue;
            boolean fix = false;
            for (Node nodeTmp : nodeList) {
                if (nodeTmp.getValue() >= valueTmp) {
                    table.put(indexAdd, nodeTmp);
                    fix = true;
                    break;
                }
            }
            if (!fix) {
                table.put(indexAdd, nodeList.get(0));
            }
        }

        node.setFingerTable(table);

    }

    private Node getNodeByValue(int value) {
        for (Node node : nodeList) {
            if (node.getValue() == value) {
                return node;
            }
        }
        return null;
    }
}


